package com.wfm.files;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 多个线程写一个文件
 * 并发问题解决：可以加文件锁。可以分别写不同区域。
 * 假设要写入一个200字节的文件，线程1写入100字节，线程2写入100字节。
 * 用MappedByteBuffer，将文件映射到内存中，然后使用不同的偏移量和长度来写入不同的区域。
 *
 * main线程等待子线程完成才完成，如果子线程只有1个则可以简单子线程名.join,但是如果用for循环，后面名字会覆盖
 * 解决：1、可以用一个list来保存子线程的变量名
 * 2、可以用线程池的shutdown和awaitTermination
 * 3、可以用CountDownLatch
 *
 */
public class FixedRegionFileWrite {

    public static void main(String[] args) throws Exception {
        method3();
    }

    private static void method1() throws IOException {
        // 文件路径
        String filePath = "output1.txt";

        // 文件总大小（确保文件足够大）
        int fileSize = 200; // 200 字节
        try (RandomAccessFile file = new RandomAccessFile(filePath, "rw")) {
            file.setLength(fileSize); // 设置文件大小
        }

        // 创建线程任务
        int threadCount = 2;
        int regionSize = fileSize / threadCount;

        List<Thread> list = new ArrayList<>();
        for (int i = 0; i < threadCount; i++) {
            long offset = i * regionSize; // 起始偏移量
            long length; // 区域长度
            if (i == threadCount - 1) {
                length = fileSize - offset; // 最后一个线程处理剩余部分
            } else {
                length = regionSize;
            }

            Thread thread = new Thread(() -> {
                try (RandomAccessFile file = new RandomAccessFile(filePath, "rw");
                     FileChannel channel = file.getChannel()) {

                    // 映射文件区域
                    MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, offset, length);

                    // 写入数据
                    String data = String.format("Thread %d writes to region %d-%d\n", Thread.currentThread().getId(), offset, offset + length - 1);
                    buffer.put(data.getBytes(StandardCharsets.UTF_8));

                    // 强制刷新到磁盘
                    channel.force(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            list.add(thread);
            thread.start();
        }

        // 等待所有线程完成
        for (Thread thread : list) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("All threads have completed writing.");
    }

    private static void method2() throws IOException, InterruptedException {
        // 文件路径
        String filePath = "output1.txt";

        // 文件总大小（确保文件足够大）
        int fileSize = 200; // 200 字节
        try (RandomAccessFile file = new RandomAccessFile(filePath, "rw")) {
            file.setLength(fileSize); // 设置文件大小
        }

        // 创建线程任务
        int threadCount = 2;
        int regionSize = fileSize / threadCount;

        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        for (int i = 0; i < threadCount; i++) {
            long offset = i * regionSize; // 起始偏移量
            long length; // 区域长度
            if (i == threadCount - 1) {
                length = fileSize - offset; // 最后一个线程处理剩余部分
            } else {
                length = regionSize;
            }
            executorService.submit(() -> {
                try (RandomAccessFile file = new RandomAccessFile(filePath, "rw");
                     FileChannel channel = file.getChannel()) {

                    // 映射文件区域
                    MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, offset, length);

                    // 写入数据
                    String data = String.format("Thread %d writes to region %d-%d\n", Thread.currentThread().getId(), offset, offset + length - 1);
                    buffer.put(data.getBytes(StandardCharsets.UTF_8));

                    // 强制刷新到磁盘
                    channel.force(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }

        // 等待所有线程完成
        executorService.shutdown();
        executorService.awaitTermination(Long.MAX_VALUE, java.util.concurrent.TimeUnit.NANOSECONDS);
        System.out.println("All threads have completed writing.");
    }
    private static void method3() throws IOException, InterruptedException {
        // 文件路径
        String filePath = "output1.txt";

        // 文件总大小（确保文件足够大）
        int fileSize = 200; // 200 字节
        try (RandomAccessFile file = new RandomAccessFile(filePath, "rw")) {
            file.setLength(fileSize); // 设置文件大小
        }

        // 创建线程任务
        int threadCount = 2;
        int regionSize = fileSize / threadCount;

        CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            long offset = i * regionSize; // 起始偏移量
            long length; // 区域长度
            if (i == threadCount - 1) {
                length = fileSize - offset; // 最后一个线程处理剩余部分
            } else {
                length = regionSize;
            }

            Thread thread = new Thread(() -> {
                try (RandomAccessFile file = new RandomAccessFile(filePath, "rw");
                     FileChannel channel = file.getChannel()) {

                    // 映射文件区域
                    MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, offset, length);

                    // 写入数据
                    String data = String.format("Thread %d writes to region %d-%d\n", Thread.currentThread().getId(), offset, offset + length - 1);
                    buffer.put(data.getBytes(StandardCharsets.UTF_8));

                    // 强制刷新到磁盘
                    channel.force(true);
                    countDownLatch.countDown();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }

        // 等待所有线程完成
        countDownLatch.await();
        System.out.println("All threads have completed writing.");
    }
}